/**
 * @file rtk_mutex.c
 * @author LokLiang (lokliang@163.com)
 * @brief
 * @version 0.1
 * @date 2023-05-01
 *
 * @copyright Copyright (c) 2023
 *
 */

#include "prv_rtk_class.h"

rtk_err_t rtk_mutex_create(rtk_mutex_t *mutex_handle, const char *name)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_ASS_FALSE(mutex_handle == NULL, "");

    rtk_err_t ret;

    do
    {
        struct rtk_mutex_handle *mutex = _rtk_name_malloc(name, sizeof(*mutex));
        if (mutex == NULL)
        {
            mutex_handle->hdl = NULL;
            ret = RTK_NOMEM;
            break;
        }

        _rtk_mutex_init(mutex);
        mutex_handle->hdl = mutex;

        ret = RTK_OK;

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_mutex_delete(rtk_mutex_t *mutex_handle)
{
    _RTK_TRACE_ENTRY();
    _RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(mutex_handle);

    size_t sched_nest = rtk_sched_suspend();

    struct rtk_mutex_handle *mutex = mutex_handle->hdl;
    struct rtk_thread_handle *hold_thread = mutex->hold_thread;

    if (hold_thread)
    {
        for (;;)
        {
            dlist_node_t *sig_node = dlist_peek_head(&hold_thread->blocked_list);

            if (sig_node)
            {
                struct rtk_thread_handle *blocked_thread = _RTK_CONTAINER_OF(sig_node, struct rtk_thread_handle, sig_node);
                _rtk_list_atomic_resume(blocked_thread);
            }
            else
            {
                break;
            }
        }

        hold_thread->mutex = mutex->last_mutex;
        mutex->hold_thread = NULL;
    }

    rtk_heapk_free(mutex);

    mutex_handle->hdl = NULL;

    rtk_sched_resume(sched_nest);

    _RTK_TRACE_EXIT();
    return RTK_OK;
}

rtk_err_t rtk_mutex_lock(rtk_mutex_t *mutex_handle, rtk_tick_t wait_ticks)
{
    _RTK_TRACE_ENTRY();
    _DO_RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(mutex_handle);
    _RTK_CHECK_SWAP(1);

    struct rtk_mutex_handle *mutex = mutex_handle->hdl;
    struct rtk_thread_handle *self_thread = curr_thread_handle;
    rtk_err_t ret;

    if (cm_rtk->nest_int || cm_rtk->nest_sched)
    {
        ret = RTK_FAIL;
        goto end;
    }

    if (wait_ticks == RTK_WAIT_FOREVER)
    {
        self_thread->wait_forever = 1;
    }
    else
    {
        self_thread->wait_forever = 0;
    }
    _RTK_TICK_LIMIT(wait_ticks);
    self_thread->wakeup_tick = cm_rtk->systime_tick + wait_ticks;

    for (;;)
    {
        rtk_sched_suspend();

        struct rtk_thread_handle *hold_thread = (struct rtk_thread_handle *)mutex->hold_thread;

        if (hold_thread == NULL)
        {
            if (CONFIG_RTK_WRN_ON != 0)
            {
                /*
                 * 死锁风险预判断
                 * 仅在解锁时记录一次锁定顺序，
                 * 因此在更复杂的使用情况中不一定能百分百分析出来
                 */
                struct rtk_mutex_handle *test_mutex = mutex;
                while ((uint)test_mutex->prev >= sizeof(void *))
                {
                    if (test_mutex->prev == mutex) // 出现倒序
                    {
                        _RTK_WRN("Discovered logic with reverse locking; there is a risk of deadlock!"); // 发现有倒序锁定的逻辑，存在死锁的风险
                        CONS_PRINT("\tthread:       '%s'\r\n"
                                   "\tmutex locked: '%s'\r\n"
                                   "\tmutex prev:   '%s'\r\n",
                                   (char *)rtk_heapk_key_base(self_thread),
                                   (char *)rtk_heapk_key_base(mutex),
                                   (char *)rtk_heapk_key_base(test_mutex));

                        test_mutex->prev = (void *)1; // 使之不再反复警告
                        break;
                    }
                    test_mutex = test_mutex->prev;
                }
            }

            mutex->nest_incr = 1;
            mutex->hold_thread = self_thread;
            mutex->last_mutex = self_thread->mutex;
            self_thread->mutex = mutex;
            rtk_sched_resume_auto();
            ret = RTK_OK;
            goto end;
        }

        if (hold_thread == self_thread) // 嵌套
        {
            if (mutex->nest_incr + 1 < mutex->nest_incr)
            {
                _RTK_WRN("Maximum nesting exceeded");
            }
            else
            {
                mutex->nest_incr++;
            }

            rtk_sched_resume_auto();
            ret = RTK_OK;
            goto end;
        }

        while (hold_thread != hold_thread->mutex->hold_thread)
        {
            if (hold_thread->mutex->hold_thread == NULL)
            {
                rtk_thread_resume((rtk_thread_t *)&hold_thread);
                break;
            }

            hold_thread = hold_thread->mutex->hold_thread;
            if (hold_thread == self_thread) // 出现死锁
            {
                if (CONFIG_RTK_WRN_ON != 0) // 打印死锁过程
                {
                    _RTK_WRN("Mutex dead lock: self-thread: '%s'", (char *)rtk_heapk_key_base(self_thread));
                    hold_thread = mutex->hold_thread;
                    while (hold_thread != hold_thread->mutex->hold_thread)
                    {
                        CONS_PRINT("     '%s' locked '%s' >>> '%s'\r\n",
                                   (char *)rtk_heapk_key_base(hold_thread),
                                   (char *)rtk_heapk_key_base(hold_thread->mutex),
                                   (char *)rtk_heapk_key_base(hold_thread->mutex->hold_thread));

                        hold_thread = hold_thread->mutex->hold_thread;
                        if (hold_thread == self_thread)
                        {
                            break;
                        }
                    }
                    CONS_PRINT("     '%s' locked '%s' >>> '%s', dead lock!\r\n",
                               (char *)rtk_heapk_key_base(self_thread),
                               (char *)rtk_heapk_key_base(mutex),
                               (char *)rtk_heapk_key_base(mutex->hold_thread));
                }

                rtk_sched_resume_auto();
                ret = RTK_FAIL;
                goto end;
            }
        }

        if ((int)(*(volatile int *)&self_thread->wakeup_tick - *(volatile int *)&cm_rtk->systime_tick) > 0)
        {
            struct rtk_mutex_handle *self_thread_mutex = self_thread->mutex;
            self_thread->mutex = mutex;
            _rtk_list_atomic_wait_mutex(self_thread);
            rtk_sched_resume_auto();
            self_thread->mutex = self_thread_mutex;

            if (rtk_heapk_is_valid(mutex) == false)
            {
                ret = RTK_TIMEOUT;
                goto end;
            }
        }
        else
        {
            rtk_sched_resume_auto();
            ret = RTK_TIMEOUT;
            goto end;
        }
    }

end:
    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_mutex_unlock(rtk_mutex_t *mutex_handle)
{
    _RTK_TRACE_ENTRY();
    _DO_RTK_CHECK_ISR();
    _RTK_CHECK_HANDLE_ERR(mutex_handle);
    _RTK_CHECK_SWAP(1);

    struct rtk_mutex_handle *mutex = mutex_handle->hdl;
    struct rtk_thread_handle *self_thread = curr_thread_handle;
    rtk_err_t ret;

    do
    {
        if (mutex->hold_thread != self_thread)
        {
            _RTK_WRN("Mutex not matched");
            ret = RTK_FAIL;
            break;
        }

        _rtk_mutex_release_unlock(mutex);

        ret = RTK_OK;

    } while (0);

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_err_t rtk_mutex_unlock_all(void)
{
    _RTK_TRACE_ENTRY();

    struct rtk_thread_handle *self_thread = curr_thread_handle;
    rtk_err_t ret = RTK_OK;

    while (self_thread->mutex)
    {
        rtk_mutex_t mutex_handle;
        mutex_handle.hdl = self_thread->mutex;
        ret = rtk_mutex_unlock(&mutex_handle);
        if (ret != RTK_OK)
        {
            break;
        }
    }

    _RTK_TRACE_EXIT();
    return ret;
}

rtk_thread_t *rtk_mutex_get_holder(rtk_mutex_t *mutex_handle)
{
    _RTK_TRACE_ENTRY();

    struct rtk_mutex_handle *mutex = mutex_handle->hdl;
    struct rtk_thread_handle *hold_thread = mutex->hold_thread;
    rtk_thread_t *ret;

    if (hold_thread == NULL)
    {
        ret = NULL;
    }
    else
    {
        ret = hold_thread->thread_handle;
    }

    _RTK_TRACE_EXIT();
    return ret;
}

bool rtk_mutex_is_valid(rtk_mutex_t *mutex_handle)
{
    _RTK_TRACE_ENTRY();
    bool ret = _rtk_service_handle_is_valid(mutex_handle);
    _RTK_TRACE_EXIT();
    return ret;
}

void _rtk_mutex_init(struct rtk_mutex_handle *mutex)
{
    mutex->hold_thread = NULL;
    mutex->last_mutex = NULL;
    mutex->nest_incr = 0;
    mutex->prev = NULL;
}

void _rtk_mutex_release_unlock(struct rtk_mutex_handle *mutex)
{
    size_t sched_nest = rtk_sched_suspend();

    _RTK_ASS_FALSE(cm_rtk->nest_sched == 0, "");
    _RTK_ASS_FALSE(mutex->nest_incr == 0, "");

    do
    {
        if (mutex->nest_incr == 0)
        {
            break;
        }

        mutex->nest_incr--;
        if (mutex->nest_incr == 0)
        {
            struct rtk_thread_handle *hold_thread = mutex->hold_thread;

            /* 如果当前解锁的不是最近锁定的，则自动将此之后的按顺序自动解锁 */
            while (hold_thread->mutex != mutex)
            {
                _rtk_mutex_release_unlock(hold_thread->mutex);
            }

            /* 所有被堵塞的线程都必需唤醒 */
            for (;;)
            {
                dlist_node_t *sig_node = dlist_peek_head(&hold_thread->blocked_list);
                if (sig_node)
                {
                    struct rtk_thread_handle *blocked_thread = _RTK_CONTAINER_OF(sig_node, struct rtk_thread_handle, sig_node);
                    _rtk_list_atomic_pend(blocked_thread);
                }
                else
                {
                    break;
                }
            }

            /* 还原状态 */
            mutex->hold_thread = NULL;
            hold_thread->mutex = mutex->last_mutex;
            if (mutex->last_mutex == NULL) // 已全部解锁
            {
                if (hold_thread->curr_prior != hold_thread->control.priority)
                {
                    hold_thread->curr_state = THREAD_STATE_RUNNING;
                    _rtk_list_atomic_pend(hold_thread);
                }
            }
            mutex->last_mutex = NULL;

            /* 记录锁定顺序 */
            if (CONFIG_RTK_WRN_ON != 0)
            {
                if (mutex->prev == NULL)
                {
                    mutex->prev = hold_thread->mutex;
                }
            }
        }

    } while (0);

    rtk_sched_resume(sched_nest);
}
